package com.CST.service.impl.ys;

import com.CST.common.BaseEntity;
import com.CST.dto.ys.YsBudgetUpDataDTO;
import com.CST.dto.ys.YsBusinessBudgetDTO;
import com.CST.dto.ys.YsBusinessBudgetDataDTO;
import com.CST.entity.ys.YsBusinessBudgetData;
import com.CST.mapper.cst.ys.YsBusinessBudgetDataMapper;
import com.CST.service.ys.YsBusinessBudgetDataService;
import com.CST.util.ExcelTransfer;
import com.CST.util.PageHelperTool;
import com.CST.vo.ys.*;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author qtx
 * @since 2023-05-22
 */
@Service
public class YsBusinessBudgetDataServiceImpl extends ServiceImpl<YsBusinessBudgetDataMapper, YsBusinessBudgetData> implements YsBusinessBudgetDataService {

    private final ExcelTransfer<YsBusinessBudgetData> excelTransfer;

    public YsBusinessBudgetDataServiceImpl(ExcelTransfer<YsBusinessBudgetData> excelTransfer) {
        this.excelTransfer = excelTransfer;
    }

    @Override
    public boolean updateYsBusinessBudgetData(YsBudgetUpDataDTO dataDTO) {
        YsBusinessBudgetData ysBusinessBudgetData = copyData(dataDTO, new YsBusinessBudgetData(), dataDTO.getMonth());
        return update(ysBusinessBudgetData, Wrappers.lambdaUpdate(YsBusinessBudgetData.class)
                .eq(YsBusinessBudgetData::getYear, dataDTO.getYear())
                .eq(YsBusinessBudgetData::getSubjectId, dataDTO.getSubjectId())
                .eq(YsBusinessBudgetData::getDepartmentId, dataDTO.getDepartmentId()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean  importExcelYsBusinessBudgetData(MultipartFile file,Integer indexYear) {
        try {
            baseMapper.deleteByYear(indexYear);
            List<YsBusinessBudgetData> ysBusinessBudgetData = baseMapper.ysBusinessData();
            Map<String, Integer> map = ysBusinessBudgetData.stream()
                    .collect(Collectors.toMap(o -> o.getSubjectCode() + o.getSubjectName(),
                            YsBusinessBudgetData::getSubjectId, (o1, o2) -> o1));
            Map<String, Integer> map1 = ysBusinessBudgetData.stream()
                    .collect(Collectors.toMap(o -> o.getDepartmentCode() + o.getDepartmentName(),
                            YsBusinessBudgetData::getDepartmentId, (o1, o2) -> o1));
            excelTransfer.importExcel(file, this, YsBusinessBudgetData.class, list -> {
                ArrayList<YsBusinessBudgetData> data = new ArrayList<>();
                AtomicReference<Integer> year = new AtomicReference<>();
                list.forEach(e -> {
                    if (year == null) {
                        year.set(Optional.ofNullable(e.getYear())
                                .orElseThrow(() -> new RuntimeException("年份字段为空。")));
                        update(Wrappers.lambdaUpdate(YsBusinessBudgetData.class)
                                .set(BaseEntity::getDeleteFlag, Boolean.TRUE)
                                .eq(YsBusinessBudgetData::getYear, year.get()));
                    }
                    boolean b1 = map.containsKey(e.getSubjectCode() + e.getSubjectName());
                    boolean b = map1.containsKey(e.getDepartmentCode() + e.getDepartmentName());
                    if (b & b1) {
                        e.setSubjectId(map.get(e.getSubjectCode() + e.getSubjectName()));
                        e.setDepartmentId(map1.get(e.getDepartmentCode() + e.getDepartmentName()));
                        caclBudgetData(e);
                        data.add(e);
                    }
                });
                return data;
            });
            return true;
        }catch (Exception e){
            throw new RuntimeException("导入失败");
        }

    }

    private static YsBusinessBudgetData caclBudgetData(YsBusinessBudgetData ysBusinessBudgetData) {
        try {
            Class<? extends YsBusinessBudgetData> clazz = ysBusinessBudgetData.getClass();
            // getDeclaredFields  可以获取本类所有的字段,包括private的,但是不能获取继承来的字段
            Field[] declaredFields = clazz.getDeclaredFields();
            // 获取get方法，判断是san 还是 xu + 月份，
            // 循环所有属性，在这里我们只计算 san xu
            for (Field declaredField : declaredFields) {
                String name = declaredField.getName();
                if ((name.contains("san") || name.contains("xu") || name.contains("all")) && name.endsWith("4")) {
                    //获取属性名称，除去4，获取类型 1 2 3 的值
                    String subName = name.substring(0, name.length() - 1);
                    String getMethodName = "get" + subName.substring(0, 1)
                            .toUpperCase(Locale.ROOT) + subName.substring(1);
                    String setMethodName = "set" + name.substring(0, 1)
                            .toUpperCase(Locale.ROOT) + name.substring(1);
                    BigDecimal type1 = (BigDecimal) clazz.getDeclaredMethod(getMethodName + 1)
                            .invoke(ysBusinessBudgetData);
                    BigDecimal type2 = (BigDecimal) clazz.getDeclaredMethod(getMethodName + 2)
                            .invoke(ysBusinessBudgetData);
                    BigDecimal type3 = (BigDecimal) clazz.getDeclaredMethod(getMethodName + 3)
                            .invoke(ysBusinessBudgetData);
                    Method declaredMethod = clazz.getDeclaredMethod(setMethodName, BigDecimal.class);
                    if (type2 != null) {
                        if (type3 == null){
                            type3 = BigDecimal.ZERO;
                        }
                        declaredMethod.invoke(ysBusinessBudgetData, type2.add(type3));
                    } else {
                        declaredMethod.invoke(ysBusinessBudgetData, type1);
                    }
                }
            }
            for (int i = 1; i < 14; i++) {
                for (int j = 1; j < 5; j++) {
                    Method setAllMethod = clazz.getDeclaredMethod("setAll" + i + j, BigDecimal.class);
                    BigDecimal sanValue = (BigDecimal) clazz.getDeclaredMethod("getSan" + i + j)
                            .invoke(ysBusinessBudgetData);
                    BigDecimal xuValue = (BigDecimal) clazz.getDeclaredMethod("getXu" + i + j)
                            .invoke(ysBusinessBudgetData);
//                    setAllMethod.invoke(ysBusinessBudgetData, (sanValue == null ? BigDecimal.ZERO : sanValue).add(
//                            xuValue == null ? BigDecimal.ZERO : xuValue));
                }
            }
            return ysBusinessBudgetData;
        } catch (Exception e) {
            throw new RuntimeException("导入失败");
        }

    }

    @Override
    public void exportExcelYsBusinessBudgetData(HttpServletResponse response, Integer year) {
        excelTransfer.exportExcel(response, baseMapper.selectNewAll(year), "业务编制数据", "sheet",
                YsBusinessBudgetData.class);
    }

    @Override
    public void exportExcelYsBusinessBudgetDataDemo(HttpServletResponse response) {
        excelTransfer.exportExcel(response, baseMapper.ysBusinessData(), "业务编制数据模板", "sheet",
                YsBusinessBudgetData.class);
    }

    @Override
    public PageInfo<YsBusinessBudgetDataVO> getBusinessDataList(YsBusinessBudgetDataDTO dto) {
        List<YsBusinessBudgetDataVO> list = baseMapper.getBusinessDataList(dto);
        return PageHelperTool.excutePageInfo(list,dto.getPageNum(),dto.getPageSize());
    }

    private void setDate(List<YsBusinessBudgetData> ysBusinessBudgetData, List<String> month, List<YsBudgetDataInfoVO> data) {
        ysBusinessBudgetData.forEach(info -> {
            YsBudgetDataInfoVO vo = new YsBudgetDataInfoVO();
            vo.setDepartmentId(info.getDepartmentId())
                    .setDepartmentCode(info.getDepartmentCode())
                    .setDepartmentName(info.getDepartmentName())
                    .setSubjectId(info.getSubjectId())
                    .setSubjectCode(info.getSubjectCode())
                    .setSubjectName(info.getSubjectName());
            List<YsBudgetDataTableVO> vos = new ArrayList<>();
            month.forEach(m -> vos.add(copyData(info, new YsBudgetDataTableVO(), m)));
            vo.setData(vos);
            data.add(vo);
        });
    }

    @SneakyThrows
    private YsBudgetDataTableVO copyData(YsBusinessBudgetData source, YsBudgetDataTableVO target, String i) {
        Class<?> sourceClass = source.getClass();
        Class<?> targetClass = target.getClass();
        for (Field field : targetClass.getDeclaredFields()) {
            String name = field.getName();
            char[] charArray = name.toCharArray();
            char c = charArray[charArray.length - 1];
            String string = new StringBuilder(name).deleteCharAt(charArray.length - 1)
                    .append(i)
                    .append(c)
                    .toString();
            String s = Optional.ofNullable(new PropertyDescriptor(string, sourceClass).getReadMethod()
                            .invoke(source))
                    .orElse("")
                    .toString();
            String invoke;
            if (s.length() > 0) {
                 invoke = new BigDecimal(s).toString();
            }else {
                invoke = "";
            }
            new PropertyDescriptor(name, targetClass).getWriteMethod()
                    .invoke(target, invoke);
        }
        return target;
    }

    @SneakyThrows
    private YsBusinessBudgetData copyData(YsBudgetUpDataDTO source, YsBusinessBudgetData target, String i) {
        Class<?> sourceClass = source.getClass();
        Class<?> targetClass = target.getClass();
        Set<String> set = Arrays.stream(targetClass.getDeclaredFields())
                .map(Field::getName)
                .collect(Collectors.toSet());
        for (Field field : sourceClass.getDeclaredFields()) {
            String name = field.getName();
            char[] charArray = name.toCharArray();
            char c = charArray[charArray.length - 1];
            String string = new StringBuilder(name).deleteCharAt(charArray.length - 1)
                    .append(i)
                    .append(c)
                    .toString();
            if (set.contains(string)) {
                BigDecimal invoke = new BigDecimal(new PropertyDescriptor(name, sourceClass).getReadMethod()
                        .invoke(source)
                        .toString());
                new PropertyDescriptor(string, targetClass).getWriteMethod()
                        .invoke(target, invoke);
            }
        }
        return target;
    }
}
